/* 
 * File:   Command.cpp
 * Author: Bram
 * 
 * Created on January 13, 2013, 12:25 AM
 */

#include "Command.h"
#include "Factory.h"
#include "Config.h"

struct xml_string_writer: pugi::xml_writer
{
    std::string result;

    virtual void write(const void* data, size_t size)
    {
        result += std::string(static_cast<const char*>(data), size);
    }
};

Command::Command() {
    setType("START");
}

Command::Command(const char* szID)
{
	Factory::assign( szID, this );
}

Command::~Command() {
}

Command * Command::Clone(){
    Command * cmd = Factory::create(getName());
    cmd->setName(getName());
    cmd->setType(getType());
    cmd->setSender(getSender());
    for(int i = 0; i < sizeParameters(); i++){
        string paramNonConst;
        getParameterAtIndex(i,paramNonConst);
        const string paramConst = paramNonConst;
        cmd->addParameter(paramConst);
    }
    return cmd;
}

//kloont zichzelf, alleen als ze van hetzelfde type zijn
bool Command::Clone(Command * inputCmd){
    
    if(inputCmd->getName().compare(getName()) == 0){
        setName(inputCmd->getName());
        setType(inputCmd->getType());
        setSender(inputCmd->getSender());
        clearParameters();
        for(int i = 0; i < inputCmd->sizeParameters(); i++){
            string paramNonConst;
            inputCmd->getParameterAtIndex(i,paramNonConst);
            const string paramConst = paramNonConst;
            addParameter(paramConst);
        }
    }
    return false;
}

const string& Command::getName()
{
    return _name;
}

void Command::setName(const string& newName)
{
    _name = newName;
}

const string& Command::getSender()
{
    return _sender;
}

void Command::setSender(const string& newSender)
{
    _sender = newSender;
}

const string& Command::getType()
{
    return _type;
}

void Command::setType(const string& newType)
{
    _type = newType;
}

void Command::addParameter(const string& newParam)
{
    Parameters.push_back(newParam);
}
void Command::removeParameter(const string& remParam)
{
    for(int i = 0; i < Parameters.size(); i++){
        if(Parameters[i].compare(remParam) == 0){
            Parameters.erase(Parameters.begin()+i);
            break;
        }
    }
}

void Command::clearParameters()
{
    Parameters.clear();
}

int Command::sizeParameters()
{
    return Parameters.size();
}

bool Command::getParameterAtIndex(int index, string& output)
{
    if(index > -1 && index < sizeParameters()){
        output = Parameters[index];
        return true;
    }
    output = "";
    return false;
}

bool Command::replaceParameterAtIndex(int index, string& output)
{
    if(index > -1 && index < sizeParameters()){
        const string constOutput = output;
        Parameters[index] = constOutput;
        return true;
    }
    output = "";
    return false;
}

Command * Command::Generate(const string& cmdName)
{
    return Factory::create(cmdName);
}

//parsed een command wat binnekomt via de socket, deze moet dus van xml omgezet worden naar een command
Command * Command::parseInputCommand(const string& strFullCommand){
    pugi::xml_document currXMLdoc;
    stringstream ssERROR;
    
    //checkt of het wel valide xml is
    if ( (Utility::getInstance()->startsWith(strFullCommand,"<?xml") || Utility::getInstance()->startsWith(strFullCommand,"<?XML")) && currXMLdoc.load(strFullCommand.c_str())){
        pugi::xml_node currCommand = currXMLdoc.child("Command");
        
        //parst naar het juiste command
        Command * cmd = Factory::create(currCommand.attribute("Name").value());
        
        cmd->setSender(currCommand.attribute("Sender").value());
        cmd->setType(currCommand.attribute("Type").value());
        
        pugi::xml_node currCommandParameters = currCommand.child("Parameters");
        
        //parst de parameters en shit
        for (pugi::xml_node_iterator parametersValues = currCommandParameters.begin(); parametersValues != currCommandParameters.end(); ++parametersValues)
        {
            for (pugi::xml_node_iterator parametersValuesREAL = parametersValues->begin(); parametersValuesREAL != parametersValues->end(); ++parametersValuesREAL)
            {
                string paramVal = parametersValuesREAL->value();
                if(paramVal.compare(Config::getInstance()->EmptyXMLNodeRepresenter) == 0){
                    paramVal = "";
                }
                Utility::getInstance()->fixPathLinuxWindowsProblem(paramVal, false);
                cmd->addParameter(paramVal);
                //cout << " '" << parametersValuesREAL->name() << "'='" << parametersValuesREAL->value() << "'" << endl;
            }
        }

        if(cmd->isValid()){
            return cmd;
        }
    } else {
        ssERROR.str("");
        ssERROR.clear();
        ssERROR << "ERROR - 3003 - XML COMMAND: '" << strFullCommand << "' NOT VALID!";
        return ErrorCommand::initializeWithErrorMessage(ssERROR.str());
    }
    return 0;
}

//command wat jij zelf ingetypt hebt wordt geparsed, deze moet dus nog verzonden worden
Command * Command::ParseOutputCommand(const string & strFullUserInputCommand)
{
    string editableUserInput = strFullUserInputCommand;
    return Factory::createFromUserInput(editableUserInput);
    /*
    bool hasZeroArguments = false;
    bool hasOneArgument = false;
    string dasCommand = strFullUserInputCommand;
    string::size_type part1POS = strFullUserInputCommand.find(":",0);
    if(part1POS == string::npos) //asl het ingevoerde command totaal verkeerd is!
    {
        stringstream ss;
        ss << "ERROR - 3003 - COMMAND: '" << strFullUserInputCommand << "' NOT VALID!";
        return ErrorCommand::initializeWithErrorMessage(ss.str());
    }
    
    
    string strCommand = strFullUserInputCommand.substr(0,part1POS);
    Utility::getInstance()->toUpper(strCommand);
    
    string::size_type firstSlashPos = strFullUserInputCommand.find("\"",0);
    if(firstSlashPos == string::npos) {hasZeroArguments = true;}
    
    string strFirstArgument;
    string strSecondArgument;
    if(!hasZeroArguments){
        dasCommand = dasCommand.substr(firstSlashPos+1);

        string::size_type secondSlashPos = dasCommand.find("\"",0);
        if(secondSlashPos == string::npos) {return 0;}
        strFirstArgument = dasCommand.substr(0,secondSlashPos);
        dasCommand = dasCommand.substr(secondSlashPos+1);

        string::size_type thirdSlashPos = dasCommand.find("\"",0);
        if(thirdSlashPos == string::npos) {hasOneArgument = true;}
        if(!hasOneArgument){
            dasCommand = dasCommand.substr(thirdSlashPos+1);

            string::size_type fourthSlashPos = dasCommand.find("\"",0);
            if(fourthSlashPos == string::npos) {
                hasOneArgument = true;
            } else {
                strSecondArgument = dasCommand.substr(0,fourthSlashPos);
            }

        }
    }
    //dasCommand = dasCommand.substr(thirdSlashPos+1);
    
    //cout << "strCommand: '" << strCommand << "'" << endl;
    //cout << "strFirstArgument: '" << strFirstArgument << "'" << endl;
    //cout << "strSecondArgument: '" << strSecondArgument << "'" << endl;
    //cout << "dasCommand: '" << dasCommand << "'" << endl;
    
    Command * cmd = Factory::create(strCommand);
    if(cmd != 0){
        cmd->setSender("CLIENT");
        cmd->setType("END"); //standaard altijd een END command wat heen gezonden wordt!

        //als er NIET 0 parameters zijn
        if(!hasZeroArguments){
            cmd->addParameter(strFirstArgument);
            if(!hasOneArgument){ //als er NIET 1 parameter is
                cmd->addParameter(strSecondArgument);
            }
        }
    } else { //als command null is
        stringstream ss;
        ss << "ERROR - 3002 - COMMAND: '" << strCommand << "' NOT FOUND!";
        cmd = ErrorCommand::initializeWithErrorMessage(ss.str());
    }
    
    return cmd;
     */
}

string Command::buildFullCommand()
{
    pugi::xml_document currXMLdoc;
    string baseCommandSTR = Config::getInstance()->getBaseCommand();
    if (currXMLdoc.load(baseCommandSTR.c_str(), pugi::parse_default))
    {
        pugi::xml_node currCommand = currXMLdoc.child("Command");
        pugi::xml_node currCommandParameters = currCommand.child("Parameters");

        pugi::xml_attribute currCommandAttribName = currCommand.attribute("Name");
        pugi::xml_attribute currCommandAttribSender = currCommand.attribute("Sender");
        pugi::xml_attribute currCommandAttribType = currCommand.attribute("Type");
        
        
        currCommandAttribName.set_value(getName().c_str());
        currCommandAttribSender.set_value(getSender().c_str());
        currCommandAttribType.set_value(getType().c_str());

        for(vector<string>::size_type i = 0; i != sizeParameters(); i++) {
            pugi::xml_node currParam = currCommandParameters.append_child("Parameter");
            string paramVal = string(Parameters[i]);
            if(paramVal.size() < 1){
                paramVal = Config::getInstance()->EmptyXMLNodeRepresenter;
            }
            currParam.append_child(pugi::node_pcdata).set_value(paramVal.c_str());
        }


        xml_string_writer writer;
        currXMLdoc.save(writer,"\t", pugi::format_raw);

        return writer.result;
    }
    cout << "ERROR in command.cpp - buildFullCommand(), should not ever happen! 88881" << endl;
    dasLog << "ERROR in command.cpp - buildFullCommand(), should not ever happen! 88881" << endl;
}
//checkt of de sender wel valide is
bool Command::isValid()
{
    //als de sender client of server is, dan is het goed
    if( ((getSender().compare("CLIENT") == 0) || (getSender().compare("SERVER") == 0)) && (Factory::exists(getName())) ){
        return true;
    }
    
    return false;
}


//haalt het volgende command van de host, haalt er de lege berichten uit van 0 bytes die er regelmatig tussen zitten
Command* Command::getNextCommandFromSocket(TcpSocket* dasSocket)
{
    bool foundRightMessage = false;
    string dasMessage;
    Command * receivedCMD = 0;
    int numBytes;
    while(!foundRightMessage){
        numBytes = dasSocket->recieveMessage(dasMessage);
        if ( numBytes == -99 ) { foundRightMessage = true; break;}
        if(numBytes > 0){
            receivedCMD = Command::parseInputCommand(dasMessage); //kan geen 0 uitkomen, altijd error msg!
            foundRightMessage = true;
        }
    }
    
    return receivedCMD;
}

void Command::PrintFullCout()
{
    string output = "";
    GetFullCout(output, false);
    cout << output;
}

void Command::GetFullCout(string& output, bool allInOneLine)
{
    stringstream ss;
    ss << (allInOneLine ? "" : "\r\n") << (allInOneLine ? "" : "\r\n") << "Print Command: " << (allInOneLine ? "" : "\r\n");
    ss << "name: '" << getName()  << "', " << (allInOneLine ? "" : "\r\n");
    ss << "sender: '" << getSender()  << "', " << (allInOneLine ? "" : "\r\n");
    ss << "type: '" << getType()  << "' " << (allInOneLine ? "" : "\r\n");
    
    for(int i = 0; i < sizeParameters(); i++){
        string param = "";
        if(getParameterAtIndex(i, param)){
            ss << " * param " << i << ": '" << param << "' " << (allInOneLine ? "" : "\r\n");
        }
    }
    
    ss << (allInOneLine ? "" : "\r\n") << (allInOneLine ? "" : "\r\n");
    output = ss.str();
}